use serde::{Deserialize, Serialize};
use std::path::PathBuf;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
    pub server: ServerSettings,
    pub database: DatabaseConfig,
    pub redis: RedisConfig,
    pub pulsar: PulsarConfig,
    pub tls: TlsConfig,
    pub monitoring: MonitoringConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerSettings {
    pub host: String,
    pub port: u16,
    pub http_port: u16,
    pub workers: usize,
    pub max_connections: usize,
    pub heartbeat_interval: u64,
    pub connection_timeout: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseConfig {
    pub url: String,
    pub max_connections: u32,
    pub min_connections: u32,
    pub connection_timeout: u64,
    pub idle_timeout: u64,
    pub max_lifetime: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RedisConfig {
    pub url: String,
    pub pool_size: usize,
    pub connection_timeout: u64,
    pub read_timeout: u64,
    pub write_timeout: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PulsarConfig {
    pub url: String,
    pub tenant: String,
    pub namespace: String,
    pub topic_prefix: String,
    pub connection_timeout: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TlsConfig {
    pub cert_file: PathBuf,
    pub key_file: PathBuf,
    pub ca_file: Option<PathBuf>,
    pub verify_client: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MonitoringConfig {
    pub prometheus_port: u16,
    pub metrics_path: String,
    pub health_check_path: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentConfig {
    pub agent: AgentSettings,
    pub server: ServerConnection,
    pub tls: TlsConfig,
    pub monitoring: MonitoringConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentSettings {
    pub id: String,
    pub hostname: String,
    pub tags: Vec<String>,
    pub capabilities: Vec<String>,
    pub heartbeat_interval: u64,
    pub reconnect_interval: u64,
    pub max_reconnect_attempts: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConnection {
    pub address: String,
    pub port: u16,
    #[serde(default)]
    pub addresses: Option<Vec<String>>, // 多Server地址，优先于单个 address/port
    pub connection_timeout: u64,
    pub retry_interval: u64,
}

impl Default for ServerConfig {
    fn default() -> Self {
        Self {
            server: ServerSettings {
                host: "0.0.0.0".to_string(),
                port: 8443,
                http_port: 8080,
                workers: num_cpus::get(),
                max_connections: 10000,
                heartbeat_interval: 30,
                connection_timeout: 60,
            },
            database: DatabaseConfig {
                url: "postgresql://localhost/oc_cluster".to_string(),
                max_connections: 20,
                min_connections: 5,
                connection_timeout: 30,
                idle_timeout: 300,
                max_lifetime: 3600,
            },
            redis: RedisConfig {
                url: "redis://localhost:6379".to_string(),
                pool_size: 10,
                connection_timeout: 5,
                read_timeout: 3,
                write_timeout: 3,
            },
            pulsar: PulsarConfig {
                url: "pulsar://localhost:6650".to_string(),
                tenant: "public".to_string(),
                namespace: "default".to_string(),
                topic_prefix: "oc_cluster".to_string(),
                connection_timeout: 10,
            },
            tls: TlsConfig {
                cert_file: PathBuf::from("certs/server.crt"),
                key_file: PathBuf::from("certs/server.key"),
                ca_file: Some(PathBuf::from("certs/ca.crt")),
                verify_client: true,
            },
            monitoring: MonitoringConfig {
                prometheus_port: 9090,
                metrics_path: "/metrics".to_string(),
                health_check_path: "/health".to_string(),
            },
        }
    }
}

impl Default for AgentConfig {
    fn default() -> Self {
        Self {
            agent: AgentSettings {
                id: uuid::Uuid::new_v4().to_string(),
                hostname: hostname::get()
                    .unwrap_or_default()
                    .to_string_lossy()
                    .to_string(),
                tags: vec![],
                capabilities: vec!["shell".to_string(), "file_transfer".to_string()],
                heartbeat_interval: 30,
                reconnect_interval: 5,
                max_reconnect_attempts: 10,
            },
            server: ServerConnection {
                address: "localhost".to_string(),
                port: 8443,
                addresses: None,
                connection_timeout: 30,
                retry_interval: 5,
            },
            tls: TlsConfig {
                cert_file: PathBuf::from("certs/client.crt"),
                key_file: PathBuf::from("certs/client.key"),
                ca_file: Some(PathBuf::from("certs/ca.crt")),
                verify_client: false,
            },
            monitoring: MonitoringConfig {
                prometheus_port: 9091,
                metrics_path: "/metrics".to_string(),
                health_check_path: "/health".to_string(),
            },
        }
    }
}

pub fn load_server_config(path: &str) -> Result<ServerConfig> {
    let settings = config::Config::builder()
        .add_source(config::File::with_name(path))
        .add_source(config::Environment::with_prefix("OC_SERVER"))
        .build()
        .map_err(|e| ManagerError::ConfigError(e.to_string()))?;

    settings.try_deserialize()
        .map_err(|e| ManagerError::ConfigError(e.to_string()))
}

pub fn load_agent_config(path: &str) -> Result<AgentConfig> {
    let settings = config::Config::builder()
        .add_source(config::File::with_name(path))
        .add_source(config::Environment::with_prefix("OC_AGENT"))
        .build()
        .map_err(|e| ManagerError::ConfigError(e.to_string()))?;

    settings.try_deserialize()
        .map_err(|e| ManagerError::ConfigError(e.to_string()))
} 